package com.google.code.swingonfire.component.foldable;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;

import javax.swing.JComponent;
import javax.swing.SwingConstants;

import com.google.code.swingonfire.animation.AbstractAnimation;
import com.google.code.swingonfire.animation.Animation;
import com.google.code.swingonfire.animation.Animator;
import com.google.code.swingonfire.image.ImageProvider;
import com.google.code.swingonfire.utility.interpolation.Interpolation;

public class JFoldableComponent extends JComponent
{

    private static final long serialVersionUID = 4331303866699581096L;

    private final JFoldablePanel panel;

    private double fraction = 1.0;
    private int expandedSize;
    private Animation animation;

    public JFoldableComponent(JFoldablePanel panel)
    {
        super();

        this.panel = panel;

        setLayout(new FoldableLayout());
    }

    public void updateState(boolean folded, boolean animate)
    {
        if (animation != null)
        {
            Animator.stop(animation);
        }

        if ((animate) && (panel.getDuration() > 0.0))
        {
            double currentFraction = getFraction();
            final double startValue = currentFraction;
            final double span = (folded) ? -currentFraction : (1 - currentFraction);
            double duration = Math.abs(span) * panel.getDuration();

            animation = new AbstractAnimation(duration)
            {

                @Override
                public double animationStep(double delay, double time, double fraction)
                {
                    setFraction(startValue + (fraction * span));
                    return 0;
                }
            };

            ImageProvider imageProvider = panel.getImageProvider();

            if (imageProvider != null)
            {
                imageProvider.updateNeeded();
            }

            Animator.start(animation);
        }
        else
        {
            setFraction((folded) ? 0.0 : 1.0);
        }
    }

    public double getFraction()
    {
        return fraction;
    }

    public double getInterpolatedFraction()
    {
        double fraction = getFraction();
        Interpolation interpolation = panel.getInterpolation();

        return (interpolation != null) ? interpolation.calculate(fraction) : fraction;
    }

    public void setFraction(double fraction)
    {
        if (this.fraction != fraction)
        {
            this.fraction = fraction;

            setSize(getPreferredSize());
            panel
                .fireFoldingInProgress(new FoldableEvent(panel, fraction, getInterpolatedFraction(), panel.isFolded()));
        }
    }

    public int getExpandedSize()
    {
        return expandedSize;
    }

    public void setExpandedSize(int expandedSize)
    {
        this.expandedSize = expandedSize;
    }

    public void pack()
    {
        Component contentComponent = panel.getContentComponent();
        Dimension preferredSize = (contentComponent != null) ? contentComponent.getPreferredSize() : new Dimension();

        expandedSize = (isFoldingHorizontal()) ? preferredSize.width : preferredSize.height;

        setPreferredSize(contentComponent.getPreferredSize());
        setMaximumSize(contentComponent.getMaximumSize());
        setMinimumSize(contentComponent.getMinimumSize());
    }

    @Override
    public Dimension getPreferredSize()
    {
        Component contentComponent = panel.getContentComponent();
        Dimension size =
            (Dimension) ((contentComponent != null) ? contentComponent.getPreferredSize().clone() : new Dimension());

        if (isFoldingHorizontal())
        {
            size.width = (int) (expandedSize * getInterpolatedFraction());
        }
        else
        {
            size.height = (int) (expandedSize * getInterpolatedFraction());
        }

        return size;
    }

    @Override
    public Dimension getMaximumSize()
    {
        Component contentComponent = panel.getContentComponent();
        Dimension size =
            (Dimension) ((contentComponent != null) ? contentComponent.getMaximumSize().clone() : new Dimension());

        if (isFoldingHorizontal())
        {
            size.width = (int) (expandedSize * getInterpolatedFraction());
        }
        else
        {
            size.height = (int) (expandedSize * getInterpolatedFraction());
        }

        return size;
    }

    @Override
    public Dimension getMinimumSize()
    {
        Component contentComponent = panel.getContentComponent();
        Dimension size =
            (Dimension) ((contentComponent != null) ? contentComponent.getMinimumSize().clone() : new Dimension());

        if (isFoldingHorizontal())
        {
            size.width = (int) (expandedSize * getInterpolatedFraction());
        }
        else
        {
            size.height = (int) (expandedSize * getInterpolatedFraction());
        }

        return size;
    }

    public boolean isFoldingHorizontal()
    {
        return ((panel.getDirection() == SwingConstants.EAST) || (panel.getDirection() == SwingConstants.WEST));
    }

    public boolean isFoldingVertical()
    {
        return ((panel.getDirection() == SwingConstants.NORTH) || (panel.getDirection() == SwingConstants.EAST));
    }

    protected boolean isShrinked()
    {
        return (fraction < 1.0);
    }

    @Override
    public void paint(Graphics g)
    {
        if (!isShrinked())
        {
            super.paint(g);

            return;
        }

        ImageProvider imageProvider = panel.getImageProvider();

        if (imageProvider != null)
        {
            Image image = imageProvider.getImage();

            if (image != null)
            {
                if (panel.getDirection() == SwingConstants.WEST)
                {
                    g.translate(-(int) (image.getWidth(null) * (1 - getInterpolatedFraction())), 0);
                }
                else if (panel.getDirection() == SwingConstants.NORTH)
                {
                    g.translate(0, -(int) (image.getHeight(null) * (1 - getInterpolatedFraction())));
                }

                g.drawImage(image, 0, 0, null);
            }
        }
    }
}
